# 导入必要的库
import tkinter as tk  # 创建GUI界面的主要库
from tkinter import messagebox, ttk  # 消息框控件和增强控件
import requests  # 用于发送HTTP请求获取天气数据
import json  # 处理JSON数据格式
import datetime  # 获取和操作日期时间
import threading  # 多线程处理
import math  # 数学运算（用于字体大小计算）
import subprocess  # 运行外部程序（打开记账本）


class DesktopWidget:
    def __init__(self, root):
        # 初始化主窗口
        self.root = root
        root.title("桌面组件")  # 设置窗口标题

        # 定义颜色方案
        bg_color = "#FFFFFF"  # 主背景色（白色）
        fg_color = "#000000"  # 前景色（黑色）
        widget_bg = "#FFFFD0"  # 小组件背景色（浅黄色）
        accent_color = "#2C78C9"  # 强调色（蓝色）

        # 根据屏幕尺寸计算基础字体大小（自适应不同分辨率）
        base_font_size = max(12, math.floor(2560 / 150))
        # 定义三种字体大小
        large_font = ('Segoe UI', base_font_size + 10, 'bold')  # 大字体用于时间显示
        normal_font = ('Segoe UI', base_font_size + 2)  # 常规字体
        small_font = ('Segoe UI', base_font_size)  # 小字体

        # 设置窗口大小和位置（宽500，高1100，定位在右上角）
        root.geometry("500x1100+1900+100")
        root.configure(bg=bg_color)  # 应用背景色
        root.attributes('-topmost', True)  # 窗口置顶显示

        # 创建主框架作为所有内容的容器（增加内边距）
        main_frame = tk.Frame(root, bg=bg_color, padx=20, pady=15)
        main_frame.pack(fill=tk.BOTH, expand=True)  # 填满整个窗口

        # ===== 时间显示区域 =====
        time_frame = tk.LabelFrame(main_frame, bg=bg_color, bd=0)  # 无边框的框架
        time_frame.pack(fill=tk.X, pady=(0, 15))  # 水平填充，底部间距

        # 创建时间显示标签
        self.time_label = tk.Label(
            time_frame,
            font=large_font,  # 使用大字体
            bg=bg_color,  # 背景色
            fg=fg_color  # 文字颜色
        )
        self.time_label.pack(anchor=tk.CENTER, pady=10)  # 居中显示

        # 添加分隔线（浅灰色）
        separator = tk.Frame(main_frame, height=2, bg="#CCCCCC")
        separator.pack(fill=tk.X, pady=8)

        # ===== 天气信息区域 =====
        # 创建带标题的框架
        weather_frame = tk.LabelFrame(
            main_frame,
            text="天气信息😎",  # 天气的标题
            font=normal_font,  # 常规字体
            bg=bg_color,  # 背景色
            fg=fg_color,  # 前景色
            padx=12,  # 内边距X轴
            pady=10  # 内边距Y轴
        )
        weather_frame.pack(fill=tk.X, pady=10)  # 水平填充

        # 天气内容容器（浅黄背景）
        weather_content = tk.Frame(weather_frame, bg=widget_bg)
        weather_content.pack(fill=tk.BOTH, padx=5, pady=5)  # 填满框架

        # 左侧文本框架
        text_frame = tk.Frame(weather_content, bg=widget_bg)
        text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 天气文字显示标签
        self.weather_text = tk.Label(
            text_frame,
            font=normal_font,  # 常规字体
            bg=widget_bg,  # 浅黄背景
            fg=fg_color,  # 黑色文字
            justify=tk.LEFT,  # 左对齐
            anchor=tk.W  # 西锚点（左对齐）
        )
        self.weather_text.pack(fill=tk.X)  # 水平填充

        # 右侧图标框架
        icon_frame = tk.Frame(weather_content, bg=widget_bg)
        icon_frame.pack(side=tk.RIGHT, padx=10, pady=10)

        # 天气图标显示标签
        self.weather_icon = tk.Label(icon_frame, bg=widget_bg)
        self.weather_icon.pack()

        # 添加分隔线
        separator2 = tk.Frame(main_frame, height=2, bg="#CCCCCC")
        separator2.pack(fill=tk.X, pady=10)

        # ===== 备忘录区域 =====
        todo_frame = tk.LabelFrame(
            main_frame,
            text="备忘录🖋️",  # 备忘录
            font=normal_font,  # 常规字体
            bg=bg_color,  # 背景色
            fg=fg_color,  # 前景色
            padx=12,  # 内边距X轴
            pady=6  # 内边距Y轴
        )
        todo_frame.pack(fill=tk.BOTH, expand=True, pady=10)  # 填满并允许扩展

        # 备忘录内容容器（浅黄背景）
        listbox_frame = tk.Frame(todo_frame, bg=widget_bg)
        listbox_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 添加垂直滚动条
        scrollbar = tk.Scrollbar(listbox_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)  # 右侧垂直填充

        # 创建待办事项列表框
        self.todo_list = tk.Listbox(
            listbox_frame,
            height=6,  # 初始显示6行
            bg=widget_bg,  # 浅黄背景
            fg=fg_color,  # 黑色文字
            selectbackground=accent_color,  # 选中项背景色
            selectforeground="#FFFFFF",  # 选中项文字色
            font=small_font,  # 小字体
            borderwidth=0,  # 无边框
            highlightthickness=0,  # 无高亮边框
            yscrollcommand=scrollbar.set  # 连接滚动条
        )
        self.todo_list.pack(fill=tk.BOTH, expand=True)  # 填满容器

        # 配置滚动条关联列表框
        scrollbar.config(command=self.todo_list.yview)

        # 添加新项目的输入区域
        input_frame = tk.Frame(todo_frame, bg=bg_color)
        input_frame.pack(fill=tk.X, pady=(15, 5))  # 水平填充，上下边距

        # 创建输入框
        self.todo_entry = tk.Entry(
            input_frame,
            bg="#CCCCCC",  # 浅灰背景
            fg=fg_color,  # 黑色文字
            font=small_font,  # 小字体
            insertbackground=fg_color,  # 光标颜色
            width=20,  # 初始宽度
            relief=tk.FLAT  # 扁平样式
        )
        self.todo_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))  # 左侧填充
        # 绑定回车键事件
        self.todo_entry.bind("<Return>", self.add_todo)

        # 添加按钮
        add_button = tk.Button(
            input_frame,
            text="添加",  # 按钮文字
            font=small_font,  # 小字体
            bg=accent_color,  # 蓝色背景
            fg="#FFFFFF",  # 白色文字
            borderwidth=0,  # 无边框
            activebackground="#3a8ce0",  # 激活状态背景色
            activeforeground="#FFFFFF",  # 激活状态文字色
            command=self.add_todo,  # 绑定事件
            width=8  # 固定宽度
        )
        add_button.pack(side=tk.LEFT)  # 放在输入框右侧

        # 删除按钮
        remove_button = tk.Button(
            todo_frame,
            text="删除选中项目",  # 按钮文字
            font=small_font,  # 小字体
            bg="#a83232",  # 红色背景
            fg="#FFFFFF",  # 白色文字
            borderwidth=0,  # 无边框
            activebackground="#c04646",  # 激活状态背景色
            activeforeground="#FFFFFF",  # 激活状态文字色
            command=self.remove_todo,  # 绑定事件
            height=1,  # 固定高度
            pady=5  # 内边距
        )
        remove_button.pack(fill=tk.X, pady=10)  # 水平填充，垂直边距

        # 添加分隔线
        separator3 = tk.Frame(main_frame, height=2, bg="#CCCCCC")
        separator3.pack(fill=tk.X, pady=10)

        # ===== 记账本区域 =====
        account_frame = tk.LabelFrame(
            main_frame,
            text="记账本📒",  # 带笔记本图标
            font=normal_font,  # 常规字体
            bg=bg_color,  # 背景色
            fg=fg_color,  # 前景色
            padx=12,  # 内边距X轴
            pady=5  # 内边距Y轴
        )
        account_frame.pack(fill=tk.X, pady=10)  # 水平填充

        # 记账本按钮
        account_button = tk.Button(
            account_frame,
            text="打开记账本",  # 按钮文字
            font=normal_font,  # 常规字体
            bg="#4CAF50",  # 绿色背景
            fg="#FFFFFF",  # 白色文字
            borderwidth=0,  # 无边框
            activebackground="#45a049",  # 激活状态背景色
            activeforeground="#FFFFFF",  # 激活状态文字色
            command=self.open_account_book,  # 绑定事件
            height=1,  # 固定高度
            width=20  # 固定宽度
        )
        account_button.pack(pady=15, padx=10, fill=tk.X)  # 垂直边距，水平填充

        #以上部分均为本人描述组件基本的结构提供给AI后由AI给出的代码，本人阅读后进行标注和简单修改

        # ===== 初始化应用程序 =====
        self.load_weather()  # 加载天气数据
        self.update_time()  # 更新时间显示
        self.load_todos()  # 加载待办事项

        # ===== 设置定时器 =====
        self.root.after(1000, self.update_time)  # 每秒更新时间
        self.root.after(1800000, self.load_weather)  # 每30分钟更新天气（1800000毫秒）

    def open_account_book(self):
        """打开记账本程序"""
        try:
            # 使用subprocess运行外部的记账本程序
            subprocess.Popen(["python", "jizhangben.py"])
        except Exception as e:
            # 错误处理：显示错误消息框
            messagebox.showerror("错误", f"无法打开记账本程序: {str(e)}")

    def update_time(self):
        """更新时间显示"""
        now = datetime.datetime.now()
        time_str = now.strftime("%H:%M:%S")  # 时间格式
        date_str = now.strftime("%Y年%m月%d日 %A")  # 日期和星期格式
        # 更新标签文本（日期在上，时间在下）
        self.time_label.config(text=f"{date_str}\n{time_str}")
        # 每秒递归调用自身更新时间
        self.root.after(1000, self.update_time)

    def load_weather(self):
        """获取和显示天气信息"""

        def fetch_weather():
            try:
                api_key = "7ba2813b70e4e8dabae24ecbded7f2b4"  # API密钥
                url = f"http://api.openweathermap.org/data/2.5/weather?q=Beijing&appid={api_key}&units=metric&lang=zh_cn"
                response = requests.get(url, timeout=5)  # 设置超时
                data = response.json()  # 解析JSON响应

                # 提取天气数据
                temp = data['main']['temp']  # 温度
                feels_like = data['main']['feels_like']  # 体感温度
                description = data['weather'][0]['description']  # 天气描述
                humidity = data['main']['humidity']  # 湿度
                wind_speed = data['wind']['speed']  # 风速

                # 获取天气图标代码
                icon_code = data['weather'][0]['icon']
                icon_url = f"http://openweathermap.org/img/wn/{icon_code}@2x.png"

                # 构建天气信息字符串
                weather_str = f"温度: {temp}℃ (体感: {feels_like}℃)\n" \
                              f"天气: {description.capitalize()}\n" \
                              f"湿度: {humidity}%\n" \
                              f"风速: {wind_speed} m/s"

                # 在主线程更新UI
                self.root.after(0, lambda: self.weather_text.config(text=weather_str))

                # 下载并显示天气图标
                try:
                    response = requests.get(icon_url, timeout=3)
                    from PIL import Image, ImageTk  # 图片处理库
                    import io  # 二进制数据操作

                    # 处理图片数据
                    image_data = io.BytesIO(response.content)
                    img = Image.open(image_data)
                    img = img.resize((80, 80))  # 调整图标大小
                    photo = ImageTk.PhotoImage(img)

                    # 更新图标显示
                    self.root.after(0, lambda: self.weather_icon.config(image=photo))
                    # 保持图片引用防止被垃圾回收
                    self.root.after(0, lambda: setattr(self.weather_icon, 'photo', photo))
                except Exception as e:
                    print("图标加载失败:", e)  # 图标加载错误时打印错误

            except Exception as e:
                print("天气获取失败:", e)
                # 更新UI显示错误信息
                self.root.after(0, lambda: self.weather_text.config(text="天气信息获取失败\n请检查网络连接"))

        # 在新线程中获取天气（避免阻塞UI）
        threading.Thread(target=fetch_weather, daemon=True).start()

    def add_todo(self, event=None):
        """添加新待办事项"""
        text = self.todo_entry.get().strip()  # 获取并清理输入
        if text:  # 确保输入不为空
            # 在列表框中添加带项目符号的条目
            self.todo_list.insert(tk.END, f"• {text}")
            self.todo_entry.delete(0, tk.END)  # 清空输入框
            self.save_todos()  # 保存到文件

    def remove_todo(self):
        """删除选中的待办事项"""
        selected = self.todo_list.curselection()  # 获取选中项索引
        if selected:  # 如果有选中的项
            self.todo_list.delete(selected[0])  # 删除第一项选中项
            self.save_todos()  # 保存到文件

    def save_todos(self):
        """保存待办事项到JSON文件"""
        try:
            # 获取所有待办项（去掉开头的项目符号）
            todos = [self.todo_list.get(idx) for idx in range(self.todo_list.size())]
            todos = [item[2:] if item.startswith("• ") else item for item in todos]

            # 写入JSON文件
            with open("todos.json", "w", encoding="utf-8") as f:
                json.dump(todos, f, ensure_ascii=False, indent=2)  # 格式化的JSON
        except Exception as e:
            print("保存待办事项时出错:", e)  # 错误处理

    def load_todos(self):
        """从文件加载待办事项"""
        try:
            with open("todos.json", "r", encoding="utf-8") as f:
                todos = json.load(f)  # 读取JSON文件
                for item in todos:
                    # 添加回带项目符号的条目
                    self.todo_list.insert(tk.END, f"• {item}")
        except FileNotFoundError:
            pass  # 文件不存在时忽略
        except Exception as e:
            print("加载待办事项时出错:", e)
            messagebox.showerror("错误", "无法加载待办事项")  # 显示错误对话框


if __name__ == "__main__":
    root = tk.Tk()  # 创建主窗口

    try:
        # 启用高DPI感知（使界面在高分屏上更清晰）这部分是AI给出的修改建议，不是我写的
        from ctypes import windll

        windll.shcore.SetProcessDpiAwareness(1)
    except Exception:
        pass  # 如果失败则忽略

    app = DesktopWidget(root)  # 创建应用实例
    root.mainloop()  # 启动主事件循环